fn main() {

    // test01();

    // test02();

    // test03();

    test04();

}

// 整型
/*
长度    有符号类型    无符号类型
8 位       i8          u8
16 位      i16         u16
32 位      i32         u32
64 位      i64         u64
128 位     i128        u128
视架构而定  isize       usize （32位CPU则为 i32/u32，64位CPU则为 i64/u64）
*/
fn test01() {
    // 关于整型溢出
    // 当在 debug 模式编译时，Rust 会检查整型溢出，若存在这些问题，则使程序在编译时 panic(崩溃,Rust 使用这个术语来表明程序因错误而退出)

    // 在当使用 --release 参数进行 release 模式构建时，Rust 不检测溢出
    /*
    相反，当检测到整型溢出时，Rust 会按照补码循环溢出（two’s complement wrapping）的规则处理。
    简而言之，大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。
    比如在 u8 的情况下，256 变成 0，257 变成 1，依此类推。程序不会 panic，
    但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码
    */

    // 要显式处理可能的溢出，可以使用标准库针对原始数字类型提供的这些方法：
    /*
    使用 wrapping_* 方法在所有模式下都按照补码循环溢出规则处理，例如 wrapping_add
    如果使用 checked_* 方法时发生溢出，则返回 None 值
    使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值
    使用 saturating_* 方法使值达到最小值或最大值
     */
    let num: i8 = 120;
    println!("{}", num.wrapping_add(10));
    println!("{:?}", num.checked_add(10));
    println!("{:?}", num.overflowing_add(10));
    println!("{}", num.saturating_add(10));

}

// 浮点型
fn test02() {
    // 浮点型计算会出现精度丢失
    let num: f32 = 0.11 + 0.22;
    let num2: f64 = 0.1 + 0.2;
    println!("f32: {} {}", num, num2);
    
    let num: f64 = 0.11 + 0.22;
    let num2: f64 = 0.1 + 0.2;
    println!("f64: {} {}", num, num2);
}

// NaN
fn test03() {
    // 数学上未定义的结果
    let x = (-42.0_f32).sqrt();
    if x.is_nan() {
        println!("未定义的数学行为")
    }
}

// 序列
fn test04() {
    // 与 Python 类似，生成一段连续的序列
    
    // 1..5 生成 1-4 的连续数字
    print!("1..5: ");
    for i in 1..5 {
        print!(" {}",i);
    }
    
    // 1..=5，生成 1-5 的连续数字
    print!("\n1..=5: ");
    for i in 1..=5 {
        print!(" {}",i);
    }

    // 序列只允许用于数字或字符类型，原因是：它们可以连续，同时编译器在编译期可以检查该序列是否为空
    // 字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型
    print!("\n'1'..'9': ");
    for c in '1'..'9' {
        print!(" {}", c)
    }
    
    print!("\n'a'..'z': ");
    for c in 'a'..'z' {
        print!(" {}", c)
    }
}

